#ifndef _EBCD_H_
#define _EBCD_H_

#include "ecm.h"
#include "ebc.h"
#include "eds.h"

#define EBCD_DEFAULT_TIMEOUT			10000
#define EBCD_DEFAULT_DETECTION_INTERVAL		10000
#define EBCD_DEFAULT_ALIVECHECK_TIMEOUT		5000

#define EBCD_IS_SELF_NDID(ndid)	((ndid) == ebcd_ndid || (ndid) == EBC_NDID_SELF)

extern ebc_mch_t	*ebcd_mch;
extern ebc_clid_t	ebcd_clid;
extern ebc_ndid_t	ebcd_ndid;
extern char		*ebcd_clname, *ebcd_ndname;
extern ebc_addr_t	ebcd_myaddr;
extern ecm_uint16	ebcd_port;
extern char		*ebcd_bdcast_addr;
extern ECMBOOL		going_to_shutdown;
extern ebc_pch_t	*pchp_accept;
extern ebc_pch_t	*pchp_bdcast;

extern ebc_selection_t	*ebcd_selection;

extern ecm_uint32	n_ext_pchs;

extern ecm_workers_t	ebcd_workers;
extern ecm_workers_t	ebcd_core_workers;

typedef enum {
	LOCAL_PCH,
	EXT_PCH,
	BDCAST_PCH
} ebcd_pch_type_t;

typedef struct {
	sr_t		*sr_patterns;
	ecm_ptr_t	lmoninfo;
	ecm_ptr_t	lcallinfo;
	ecm_ptr_t	peerinfo;
} lpch_t;

typedef struct {
	ecm_ptr_t	emoninfo;
	ecm_ptr_t	ecallinfo;
} epch_t;

typedef struct {
	ebcd_pch_type_t	type;
	ebc_pch_t	*pchp;
	ebc_pktb_t	*pktb_aliveness;
	ecm_uint32	refcnt;
	ecm_mutex_t	mutex;
	ECMBOOL		destroyed;
	ECMBOOL		alivecheck;
	ECMBOOL		routing;
	ecm_uint64	last_recv, last_sent;
	ecm_uint32	n_routing_pktbs;
	struct list_head	routing_pktbs;
	struct list_head	list;	/* accepted list */
	struct list_head	list_recv, list_sent;
	union {
		lpch_t	local;
		epch_t	ext;
	} u;
} ebcd_pch_t;

/* function declarations *****************************************************/

extern ebcd_pch_t	*dpch_bdcast;
extern ecm_uint32	ebcd_alivecheck_timeout;
extern ecm_int32	ebcd_detection_interval;

void ebcd_get_clid(const char *clname, ebc_clid_t *pclid);

ebcd_pch_t *ebcd_add_pch(ebc_pch_t *pchp);
void ebcd_del_pch(ebcd_pch_t *dpch);
void ebcd_accept_pch(ebcd_pch_t *dpch);
void ebcd_broadcast_pktb(ebcd_pch_t *dpch_src, ebc_pktb_t *pktb);
sr_t *ebcd_get_all_ndids(void);

ebcd_pch_t *create_ebcd_pch(ebc_pch_t *pchp, ebcd_pch_type_t type);

ebcd_pch_t *ebcd_add_epch(ebc_pch_t *pchp);
void ebcd_free_epch(ebcd_pch_t *dpch);
ebcd_pch_t *ebcd_get_epch(ebc_ndid_t ndid);
ebcd_pch_t *ebcd_find_epch(ebc_ndid_t ndid);
ECMBOOL ebcd_has_epch(ebc_ndid_t ndid);
void start_monitor_epch(ebc_ndid_t ndid, ebc_mdid_t mdid, ebc_mdid_t mdid_mon);
void stop_monitor_epch(ebc_ndid_t ndid, ebc_mdid_t mdid, ebc_mdid_t mdid_mon);

ebcd_pch_t *ebcd_add_lpch(ebc_pch_t *pchp);
void ebcd_free_lpch(ebcd_pch_t *dpch);
ebcd_pch_t *ebcd_get_lpch(ebc_mdid_t mdid);
ebcd_pch_t *ebcd_find_lpch(ebc_mdid_t mdid);
ECMBOOL ebcd_add_patterns(sr_t *sr_patterns);
void ebcd_del_patterns(sr_t *sr_patterns);

void ebcd_write_pchs();
void ebcd_read_pchs(void);
void ebcd_wait_reader(void);

void ebcd_accept(ECMBOOL ebcd_only);

void ebcd_process_pktb(ebcd_pch_t *dpch, ebc_pktb_t *pktb);
void ebcd_switch_pktb(ebcd_pch_t *dpch_src, ebc_pktb_t *pktb);
void ebcd_build_route(ebcd_pch_t *dpch, ebc_pktb_t *pktb);

ebc_err_t ebcd_add_route(const char *addrspec);
ebc_err_t ebcd_del_route(const char *addrspec);

void ebcd_destroy_pch(ebcd_pch_t *dpch);

void ebcd_start_router(void);
void ebcd_stop_router(void);
void ebcd_add_lazy_pktb(ebcd_pch_t *dpch, ebc_pktb_t *pktb);

void ebcd_start_reader(void);
void ebcd_stop_reader(void);

void ebcd_start_alivechecker(void);
void ebcd_stop_alivechecker(void);
void ebcd_init_alivecheck(ebcd_pch_t *dpch);
void ebcd_clear_alivecheck(ebcd_pch_t *dpch);

void ebcd_start_detecter(void);
void ebcd_stop_detecter(void);

void process_detection(ebcd_pch_t *dpch, ebc_pktb_t *pktb);

void ebcd_exec_mcb(ebc_mch_t *mch, ebc_msg_t *msg);

ECMBOOL ebcd_initialize_pchs(void);
void ebcd_finalize_pchs(void);

void ebcd_initialize_mch(void);
void ebcd_initialize_route(void);
void ebcd_initialize_monitor(void);

ebcd_pch_t *get_routed_pch(ebc_addr_t *daddr);

ECMBOOL ebcd_pch_get(ebcd_pch_t *dpch);
void ebcd_pch_put(ebcd_pch_t *dpch);

void ebcd_app_loop(void);

ebc_err_t ebcd_send_aliveness(ebcd_pch_t *dpch);

void cancel_initial_detection(void);

ebc_pktb_t *ebcd_build_pktb(ebc_addr_t *paddr_src, ebc_addr_t *paddr_dst, sr_t *sr_data, ebc_msgt_t msgt);
ebc_err_t ebcd_push_pktb(ebcd_pch_t *dpch, ebc_pktb_t *pktb);
void ebcd_drop_pktb(ebc_pktb_t *pktb);
ebc_err_t ebcd_push_msg(ebcd_pch_t *dpch, ebc_msg_t *msg);
ebc_err_t ebcd_send_msg(ebcd_pch_t *dpch, ebc_msg_t *msg);

void ebcd_push_errmsg(ebc_addr_t *saddr, ebc_addr_t *daddr, ebc_msgid_t msgid, ecm_uint32 err_msgt, ECMBOOL req);
ECMBOOL ebcd_push_bdcastack(ebcd_pch_t *dpch, ebc_mhdr_t *mhdr);
ebc_err_t ebcd_call(ebc_addr_t *daddr, sr_t **psr, ebc_msgt_t msgt, ecm_uint32 timeout);

void ebcd_cancel_msg(ebcd_pch_t *dpch);

void set_MRU_recv_dpch(ebcd_pch_t *dpch);
void set_MRU_sent_dpch(ebcd_pch_t *dpch);

void change_static_route_status(ebc_ndid_t ndid, ECMBOOL established);
void do_active_detection(void);

#ifdef ECM_DEBUG

extern ECMBOOL		ebcd_simple_test;

extern ecm_dbg_t	ebcd_dbg;
#define	DBG_EBCD	0x0001
#define DBG_PCH		0x0002
#define DBG_DPCH	0x0004
#define DBG_ROUTE	0x0008
#define DBG_EXTCONN	0x0010
#define DBG_PKTDROP	0x0020
#define DBG_BDCAST	0x0040
#define DBG_ALIVECHECK	0x0080
#define DBG_ALL		0xffff

extern const char *ebcd_dbg_pch(ebcd_pch_t *);

#define EBCDDBG(dbgset, format, ...)	ECMDBG(ebcd_dbg, dbgset, format, ## __VA_ARGS__)

#define EBCD_PCH_GET_ASSERT(dpch)	ECMASSERT(ebcd_pch_get(dpch))

#else /* !ECM_DEBUG */

#define EBCDDBG(dbgset, fmt, ...)       do {} while(0)

#define EBCD_PCH_GET_ASSERT(dpch)	ebcd_pch_get(dpch)

#endif /* ECM_DEBUG */

#endif
